package modellog

import (
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
	"gogame/game/manage/model"
	"gogame/gameconfig"
	"gogame/gameservice"
	"gogame/gameservice/gateway"
	"gogame/gameservice/service"
	"gogame/lib/database/mongo"
	"gogame/logger"
	"sync"
	"time"
)

type Service struct {
	service.Service
	Mdb           *mongo.MongoStruct
	stopMu        sync.Mutex
	isStop        bool               // 是否接受外部stop
	stop          chan bool          // 是否停止处理
	tableName     string             // 存储model_log的表
	sleepSecond   time.Duration      // 数据处理间隔
	Limit         int64              // 单次处理上限
	handleUidList chan string        // 需要优先处理的玩家
	RpcManage     *gateway.RpcManage // rpc管理
}

func NewService() service.IService {
	_service := &Service{
		stop:          make(chan bool, 1),
		tableName:     "model_log",
		sleepSecond:   2,
		handleUidList: make(chan string, 10000),
	}
	if gameconfig.ServerConfig.GetGameVer() == "cross" {
		_service.Mdb = mongo.InitCrossMongo()
	} else {
		_service.Mdb = mongo.InitMongo()
	}
	_service.RpcManage = gateway.NewRpcManage(_service)

	return _service
}

func init() {
	gameservice.Service2NewFunc["modellog"] = NewService
}

// StartServer 开始服务
func (s *Service) StartServer(serviceName, listen string) {
	// 初始化日志
	logger.InitLog("modellog", serviceName)
	// rpcManage开启
	s.RpcManage.Run(serviceName, "modelLog:-1")
	// 处理model_log
	go s.Run()
}

// StopServer 停止服务
func (s *Service) StopServer() {
	s.stopMu.Lock()
	defer s.stopMu.Unlock()

	if s.isStop {
		return
	}

	s.isStop = true
	s.stop <- true
	s.RpcManage.Stop()
}

// HandleNotice 处理服务通知 主要用于玩家离线worker通知到优先保存该玩家数据
func (s *Service) HandleNotice(data []any) {
	if len(data) == 0 {
		return
	}

	// 第一个参数必须是方法名
	_funcName, ok := data[0].(string)
	if !ok {
		return
	}

	switch _funcName {
	// 玩家离线保存玩家数据
	case "NoticeSaveUserData":
		s.NoticeSaveUserData(data[1].(string))
	}
}

// Run 开始处理model_log
func (s *Service) Run() {
	_ticker := time.NewTicker(time.Second * s.sleepSecond)
	defer _ticker.Stop()
	for {
		select {
		// 处理所有model_log
		case <-_ticker.C:
			s.HandleAll()
		// 处理指定玩家model_log
		case uid := <-s.handleUidList:
			s.HandleUser(uid)
		case <-s.stop:
			return
		}
	}
}

// NoticeSaveUserData 通知报错玩家数据
func (s *Service) NoticeSaveUserData(uid string) {
	s.handleUidList <- uid
}

/*
---------------------------------------------------------------------------------------------------------------------
*/

// GetModelLogList 获取待处理的modelLogList
func (s *Service) GetModelLogList(filter bson.M) []*model.ModelLog {
	var modelLogList []*model.ModelLog

	// 排除掉存在err的
	filter["err"] = bson.M{
		"$exists": false,
	}
	s.Mdb.FindByReflect(s.tableName, filter, &modelLogList, options.Find().SetLimit(s.Limit))

	return modelLogList
}

// HandleAll 处理所有数据
func (s *Service) HandleAll() {
	s.HandleModelLogList(s.GetModelLogList(bson.M{}))

}

// HandleUser 处理的那个玩家数据
func (s *Service) HandleUser(uid string) {
	s.HandleModelLogList(s.GetModelLogList(bson.M{"uid": uid}))
}

// HandleModelLogList 处理modelLogList
func (s *Service) HandleModelLogList(modelLogList []*model.ModelLog) {
	defer func() {
		if err := recover(); err != nil {
			logger.ModelLogLog.Warn(fmt.Sprintf("modelLog HandleModelLogList err!!\n%s", err))
		}
	}()

	// 还没有数据需要处理
	if len(modelLogList) == 0 {
		return
	}

	var _delIdList []string
	for _, modelLog := range modelLogList {
		err := s.HandleModelLog(modelLog)
		// 没有异常，执行成功，清除
		if err == nil {
			_delIdList = append(_delIdList, modelLog.Id)
		} else {
			// 异常，写入mongo，保留
			s.Mdb.Update(
				s.tableName,
				bson.M{"_id": modelLog.Id},
				map[string]any{
					"err": err.Error(),
				},
			)
		}
	}

	// 删除执行成功的日志
	if len(_delIdList) > 0 {
		s.Mdb.Delete(
			s.tableName,
			bson.M{
				"_id": bson.M{
					"$in": _delIdList,
				},
			},
		)
	}
}

// HandleModelLog 处理modelLog
func (s *Service) HandleModelLog(modelLog *model.ModelLog) error {
	var err error
	switch modelLog.Act {
	case "Set":
		_, err = s.Mdb.Update(modelLog.Table, BsonDToBsonM(modelLog.D[0]), BsonDToMap(modelLog.D[1]))
	case "Remove":
		_, err = s.Mdb.Delete(modelLog.Table, BsonDToBsonM(modelLog.D[0]))
	case "InsertOne":
		_, err = s.Mdb.InsertOne(modelLog.Table, modelLog.D[0])
	case "InsertMany":
		_, err = s.Mdb.InsertMany(modelLog.Table, BsonDListToMapList(modelLog.D))
	}

	if err != nil {
		logger.ModelLogLog.Warn(fmt.Sprintf("modellog.HandleModelLog %s fail!\nerr -->%s", modelLog.Act, err.Error()))
	}

	return err
}
